//
//  ZQRCodeScanner.m
//  WeClubs
//
//  Created by Zed Link on 27/3/2017.
//  Copyright © 2017 Zed. All rights reserved.
//
//  Info.plist文件需要添加"NSCameraUsageDescription"

#import "ZQRCodeScanner.h"
#import "ZVerticalButton.h"
#import <AVFoundation/AVFoundation.h>
#import <CoreImage/CoreImage.h>
#import <ImageIO/ImageIO.h>

#define ScanBoxTopSpace 140
#define ScanBoxLeftSpace 63
#define ScanlineTopCorrection 8.0
#define ScanLineAnimationDuration 2.0
#define ScanLineAnimationKey @"ScanLineAnimationKey"

@interface ZQRCodeScanner () <AVCaptureMetadataOutputObjectsDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>

/** 二维码扫描 */
@property (strong, nonatomic) AVCaptureDevice *camera;
@property (strong, nonatomic) AVCaptureSession *session;
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *previewLayer;

/** 图片二维码识别 */
@property (strong, nonatomic) CIDetector *detector;
@property (strong, nonatomic) CIContext *context;

/** 扫描器是否启动成功 */
@property (assign, nonatomic) BOOL isRunningSuccess;

/** 扫描线 */
@property (weak, nonatomic) UIImageView *scanLine;
/** 遮盖图层 */
@property (weak, nonatomic) UIView *coverView;

@end

@implementation ZQRCodeScanner

#pragma mark - About Navigation

- (void)zConfigNavigation
{
    [super zConfigNavigation];
    [self zClearNavBgColorAndSeparator:NO];
    
    self.navigationItem.title = @"扫一扫";
    self.navigationItem.leftBarButtonItem = [self zNavItemWithImage:@"icon_return" shouldOriginal:YES andSelector:@selector(zPopBack)];
}

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

#pragma mark - Load SubViews

- (void)zLoadSubview
{
    [super zLoadSubview];
    
    // ========== 遮盖视图 ==========
    UIView *coverView = [[UIView alloc] init];
    [coverView setBackgroundColor:[UIColor clearColor]];
    [self.view addSubview:coverView];
    _coverView = coverView;
    
    [coverView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));
    }];
    
    CGFloat squareWH = self.view.width - ScanBoxLeftSpace * 2;
    // ========== 扫描框 ==========
    UIImageView *scanBox = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"scan_box"]];
    [self.view addSubview:scanBox];
    
    [scanBox mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(UIEdgeInsetsMake(ScanBoxTopSpace, ScanBoxLeftSpace, self.view.height - ScanBoxTopSpace - squareWH, ScanBoxLeftSpace));
    }];
    
    // ========== 扫描线 ==========
    UIImageView *scanLine = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"scan_line"]];
    [scanBox addSubview:scanLine];
    _scanLine = scanLine;
    
    [scanLine mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.mas_equalTo(scanBox);
        make.top.mas_equalTo(scanBox).offset(ScanlineTopCorrection);
    }];
    
    // ==========介绍标签 ==========
    UILabel *introduce = [UILabel zLabelWithSize:13 textColor:QRCODE_SCANNER_FG_COLOR text:@"将二维码放入框内, 即可自动扫描"];
    [self.view addSubview:introduce];
    
    [introduce mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.mas_equalTo(scanBox.mas_bottom).offset(15);
    }];
    
    // ========== 我的二维码 ==========
    UIButton *myQRCode = [UIButton zButtonWithTitle:@"我的二维码" fontSize:16 textColor:HIGHTLIGHT_COLOR];
    [self.view addSubview:myQRCode];
    
    [myQRCode mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.mas_equalTo(introduce.mas_bottom).offset(16);
    }];
    
    // ========== 相册 ==========
    CGFloat btnWH = 88, space = 40;
    CGFloat offsetX = (self.view.width - space - btnWH * 2) / 2;
    
    ZVerticalButton *album = [ZVerticalButton zVerticalButtonWithTitle:@"相册" fontSize:13 color:QRCODE_SCANNER_FG_COLOR image:@"ic_scan_picture"];
    album.space = 8;
    [self.view addSubview:album];
    
    [album addTarget:self action:@selector(albumAction) forControlEvents:UIControlEventTouchUpInside];
    
    [album mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.view.mas_left).offset(offsetX);
        make.bottom.mas_equalTo(self.view).offset(-36);
        make.width.height.mas_equalTo(btnWH);
    }];
    
    // ========== 闪光灯 ==========
    ZVerticalButton *flashLight = [ZVerticalButton zVerticalButtonWithTitle:@"闪光灯" fontSize:13 color:QRCODE_SCANNER_FG_COLOR image:@"ic_scan_light_nor"];
    flashLight.space = 8;
    [self.view addSubview:flashLight];
    
    [flashLight addTarget:self action:@selector(flashLightAction:) forControlEvents:UIControlEventTouchUpInside];
    [flashLight setImage:[UIImage imageNamed:@"ic_scan_light_sel"] forState:UIControlStateSelected];
    
    [flashLight mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_equalTo(self.view.mas_right).offset(-offsetX);
        make.bottom.mas_equalTo(self.view).offset(-36);
        make.width.height.mas_equalTo(btnWH);
    }];
}

#pragma mark - View Life Cycle

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSError *error;
    _isRunningSuccess = YES;
    _session = [[AVCaptureSession alloc] init];
    
    // ========== 输入端 ==========
    _camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    AVCaptureDeviceInput *cameraInput = [AVCaptureDeviceInput deviceInputWithDevice:_camera error:&error];
    if (error) {
        ZLog(@"二维码扫描器: 获取摄像头失败, %@", error);
        _isRunningSuccess = NO;
        return;
    }
    
    if ([_session canAddInput:cameraInput]) {
        [_session addInput:cameraInput];
    } else {
        ZLog(@"二维码扫描器: 输入端添加失败, %@", error);
        _isRunningSuccess = NO;
        return;
    }
    
    // ========== 输出端 ==========
    AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
    if ([_session canAddOutput:metadataOutput]) {
        [_session addOutput:metadataOutput];
    } else {
        ZLog(@"二维码扫描器: 输出端添加失败, %@", error);
        _isRunningSuccess = NO;
        return;
    }
    
    [metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_global_queue(0, 0)];
    
    ZLog(@"metadataOutput.availableMetadataObjectTypes:\n%@", metadataOutput.availableMetadataObjectTypes);
    metadataOutput.metadataObjectTypes = @[@"org.iso.QRCode"];
    
    // 计算扫描框位置
    CGFloat scanBoxWH = self.view.width - ScanBoxLeftSpace * 2;
    CGFloat horizontalSpace = (self.view.width - scanBoxWH) / 2;
    
    // 限制扫描范围
    CGFloat rectX = horizontalSpace / self.view.width;
    CGFloat rectY = ScanBoxTopSpace / self.view.height;
    CGFloat rectWidth = scanBoxWH / self.view.width;
    CGFloat rectHeight = scanBoxWH / self.view.height;
    
    metadataOutput.rectOfInterest = CGRectMake(rectY, rectX, rectHeight, rectWidth);
    
    // ========== 预览图层 ==========
    _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    [self.view.layer insertSublayer:_previewLayer atIndex:0];
    
    [self zAddCoverWithTopSpace:ScanBoxTopSpace horizantalSpace:horizontalSpace SquareSide:scanBoxWH];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    // 设置导航栏为扫描器样式
    [self zSetNavigationForceColor:QRCODE_SCANNER_FG_COLOR];
    
    // ========== 启动会话 ==========
    if ([_session isRunning] == NO && _isRunningSuccess == YES) {
        [_session startRunning];
    }
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    if (_isRunningSuccess == NO) {
        // 启动失败, 弹窗提示
        UIAlertController *remind = [UIAlertController alertControllerWithTitle:nil message:@"二维码扫描器启动失败!" preferredStyle:UIAlertControllerStyleAlert];
        [remind addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]];

        [self presentViewController:remind animated:YES completion:nil];
    }
    
    // ========== 添加扫描线动画 ==========
    CGFloat destY = _scanLine.width - ScanlineTopCorrection * 2, originY = _scanLine.frame.origin.y;
    
    CAKeyframeAnimation *scanAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
    scanAnimation.duration = ScanLineAnimationDuration;
    scanAnimation.values = @[@(originY), @(destY)];
    scanAnimation.repeatCount = MAXFLOAT;
    
    [_scanLine.layer addAnimation:scanAnimation forKey:ScanLineAnimationKey];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    // 恢复导航栏样式为默认
    [self zSetNavigationForceColor:NAVBAR_FG_COLOR];
    
    // ========== 关闭会话 ==========
    if ([_session isRunning]) {
        [_session stopRunning];
    }
    
    // 移除扫描线动画
    [_scanLine.layer removeAnimationForKey:ScanLineAnimationKey];
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    
    _previewLayer.frame = self.view.layer.bounds;
}

#pragma mark - Private Method

- (void)zAddCoverWithTopSpace:(CGFloat)top horizantalSpace:(CGFloat)horizantol SquareSide:(CGFloat)squareSide
{
    [self zAddCoverLayerWithRect:CGRectMake(0, 0, self.view.width, ScanBoxTopSpace)];
    [self zAddCoverLayerWithRect:CGRectMake(0, ScanBoxTopSpace, horizantol, squareSide)];
    [self zAddCoverLayerWithRect:CGRectMake(horizantol + squareSide, ScanBoxTopSpace, horizantol, squareSide)];
    [self zAddCoverLayerWithRect:CGRectMake(0, ScanBoxTopSpace + squareSide, self.view.width, self.view.height - ScanBoxTopSpace - squareSide)];
}

- (void)zAddCoverLayerWithRect:(CGRect)rect
{
    CAShapeLayer *topLayer = [CAShapeLayer layer];
    topLayer.path = [UIBezierPath bezierPathWithRect:rect].CGPath;
    topLayer.opacity = 0.5;
    topLayer.fillColor = [UIColor zColorWithHex:0x000000].CGColor;
    [_coverView.layer addSublayer:topLayer];
}

#pragma mark - Torch Control

- (void)zTurnTorchOn:(BOOL)isON
{
    
    if (_camera.hasTorch == NO || _camera.torchAvailable == NO) {
        ZLog(@"设备不支持闪光灯");
        // Todo: 不支持闪光灯的提示
        return;
    }
    
    [_camera lockForConfiguration:nil];
    
    // Torch用于闪光灯的持续开启, 如视频录制 (一直亮)
    [_camera setTorchMode:isON ? AVCaptureTorchModeOn : AVCaptureTorchModeOff];
    // Flash用于闪光灯的短暂发亮, 如照片拍摄 (闪一下)
//    [_camera setFlashMode:isON ? AVCaptureFlashModeOn : AVCaptureFlashModeOff];
    
    [_camera unlockForConfiguration];
}

#pragma mark - QRCode Detector

- (void)detectorQRCodeFromImage:(UIImage *)image
{
    CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];
    NSNumber *imageOrientation = [inputImage.properties valueForKey:CFBridgingRelease(kCGImagePropertyOrientation)];
    
    NSArray *features;
    if (imageOrientation == nil) {
        features = [self.detector featuresInImage:inputImage];
    } else {
        features = [self.detector featuresInImage:inputImage options:@{CIDetectorImageOrientation : imageOrientation}];
    }
    
    for (CIQRCodeFeature *feature in features) {
        ZLog(@"QRCode From Image: %@", feature.messageString);
        
        [self dealWithMessage:feature.messageString];
    }
}

#pragma mark - QRCode Message handle

- (void)dealWithMessage:(NSString *)message
{
    // Todo: 二维码内容处理
    UIAlertController *remind = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
    [remind addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]];
    
    [self presentViewController:remind animated:YES completion:nil];
}

#pragma mark - AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    for (AVMetadataMachineReadableCodeObject *codeObject in metadataObjects) {
        ZLog(@"扫描结果: %@", codeObject.stringValue);
        
        [self dealWithMessage:codeObject.stringValue];
        
        [_session stopRunning];
    }
}

#pragma mark - UIImagePickerControllerDelegate

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    ZLog(@"info: %@", info);
    
    UIImage *image = info[UIImagePickerControllerOriginalImage];
    
    [self dismissViewControllerAnimated:YES completion:^{
        [self detectorQRCodeFromImage:image];
    }];
}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - Cotrol Action

- (void)myQRCodeAction
{
    // Todo: 我的二维码
}

- (void)albumAction
{
    // ========== 用户相册 ==========
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.delegate = self;
    
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {
        imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
        
        ZLog(@"mediaType: %@", [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum]);
        imagePicker.mediaTypes = @[@"public.image"];
    }
    
    [self presentViewController:imagePicker animated:YES completion:nil];
}

- (void)flashLightAction:(ZVerticalButton *)sender
{
    sender.selected = !sender.selected;
    [self zTurnTorchOn:sender.selected];
}

#pragma mark - Getter & Setter

- (CIDetector *)detector
{
    if (_detector == nil) {
        _detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:self.context options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
    }
    return _detector;
}

- (CIContext *)context
{
    if (_context == nil) {
        _context = [CIContext context];
    }
    return _context;
}

@end
